package org.example.liuy.handler;

import lombok.extern.slf4j.Slf4j;
import org.example.liuy.annotation.RestController;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@Slf4j
public class ComponentScannerHelper {

    public static Set<Class<?>> scanControllers(String packageName) {
        Set<Class<?>> controllers = new HashSet<>();
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            String path = packageName.replace('.', '/');
            Enumeration<URL> resources = classLoader.getResources(path);

            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                String protocol = resource.getProtocol();

                if ("file".equals(protocol)) {
                    // 处理文件系统中的类（开发环境）
                    File directory = new File(URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8));
                    if (directory.exists()) {
                        scanDirectory(directory, packageName, controllers);
                    }
                } else if ("jar".equals(protocol)) {
                    // 处理JAR文件中的类（运行环境）
                    scanJarFile(resource, packageName, controllers);
                }
            }
        } catch (Exception e) {
            log.error("Error scanning components", e);
        }
        return controllers;
    }

    private static void scanDirectory(File directory, String packageName, Set<Class<?>> controllers) {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    scanDirectory(file, packageName + "." + file.getName(), controllers);
                } else if (file.getName().endsWith(".class")) {
                    String className = packageName + "." + file.getName().substring(0, file.getName().length() - 6);
                    try {
                        Class<?> clazz = Class.forName(className);
                        if (clazz.isAnnotationPresent(RestController.class)) {
                            controllers.add(clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        // Ignore
                        log.error("Error loading class", e);
                    }
                }
            }
        }
    }

    private static void scanJarFile(URL resource, String packageName, Set<Class<?>> controllers) {
        try {
            JarURLConnection jarConnection = (JarURLConnection) resource.openConnection();
            JarFile jarFile = jarConnection.getJarFile();
            String packagePath = packageName.replace('.', '/');

            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                String entryName = entry.getName();

                // 检查是否是目标包下的类文件
                if (entryName.startsWith(packagePath) && entryName.endsWith(".class") && !entryName.contains("$")) {
                    // 将文件路径转换为类名
                    String className = entryName.replace('/', '.').substring(0, entryName.length() - 6);
                    try {
                        Class<?> clazz = Class.forName(className);
                        if (clazz.isAnnotationPresent(RestController.class)) {
                            controllers.add(clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        // Ignore
                        log.error("Error loading class from jar: {}", className, e);
                    } catch (NoClassDefFoundError e) {
                        // Ignore
                        log.error("NoClassDefFoundError for class: {}", className, e);
                    }
                }
            }
        } catch (IOException e) {
            log.error("Error scanning jar file", e);
        }
    }

    /**
     * 实例化控制器类
     * @param controllerClass 控制器类
     * @param dependencies 依赖对象数组
     * @return 控制器实例
     */
    public static Object instantiateController(Class<?> controllerClass, Object... dependencies) {
        try {
            // 尝试匹配构造函数
            var constructors = controllerClass.getDeclaredConstructors();
            for (var constructor : constructors) {
                Class<?>[] constructorParams = constructor.getParameterTypes();

                // 尝试使用提供的依赖项匹配构造函数参数
                Object[] matchedDependencies = matchDependencies(constructorParams, dependencies);
                if (matchedDependencies != null) {
                    constructor.setAccessible(true);
                    return constructor.newInstance(matchedDependencies);
                }
            }

            // 如果没有匹配的构造函数，抛出异常
            throw new RuntimeException("No suitable constructor found for: " + controllerClass.getName());
        } catch (Exception e) {
            log.error("Error instantiating controller: {}", controllerClass.getName(), e);
            throw new RuntimeException("Failed to instantiate controller: " + controllerClass.getName(), e);
        }
    }

    /**
     * 根据构造函数参数类型匹配依赖项
     * @param paramTypes 构造函数参数类型
     * @param dependencies 可用的依赖项
     * @return 匹配的依赖项数组，如果无法匹配则返回null
     */
    private static Object[] matchDependencies(Class<?>[] paramTypes, Object[] dependencies) {
        if (paramTypes.length > dependencies.length) {
            return null; // 依赖项不够
        }

        Object[] matchedDependencies = new Object[paramTypes.length];

        // 用于标记依赖项是否已被使用
        boolean[] usedDependencies = new boolean[dependencies.length];

        // 为每个参数类型找到匹配的依赖项
        for (int i = 0; i < paramTypes.length; i++) {
            Class<?> paramType = paramTypes[i];
            boolean found = false;

            // 查找匹配的依赖项
            for (int j = 0; j < dependencies.length; j++) {
                if (!usedDependencies[j] && isAssignable(paramType, dependencies[j].getClass())) {
                    matchedDependencies[i] = dependencies[j];
                    usedDependencies[j] = true;
                    found = true;
                    break;
                }
            }

            if (!found) {
                return null; // 无法为当前参数找到匹配的依赖项
            }
        }

        return matchedDependencies;
    }

    /**
     * 检查目标类型是否可以分配源类型
     * @param targetType 目标类型
     * @param sourceType 源类型
     * @return 是否可以分配
     */
    private static boolean isAssignable(Class<?> targetType, Class<?> sourceType) {
        if (targetType.isAssignableFrom(sourceType)) {
            return true;
        }

        // 处理接口实现情况
        if (targetType.isInterface()) {
            Class<?>[] interfaces = sourceType.getInterfaces();
            for (Class<?> iface : interfaces) {
                if (iface == targetType || isAssignable(targetType, iface)) {
                    return true;
                }
            }

            // 检查父类的接口
            Class<?> superClass = sourceType.getSuperclass();
            if (superClass != null && superClass != Object.class) {
                return isAssignable(targetType, superClass);
            }
        }

        return false;
    }
}